﻿using System;
using System.Collections.Generic;

using System.Text;

namespace JustMathCore
{
    public static class HexTool
    {
        public static byte Hexstr2Uint8(string input)
        {
            if (input.ToLower().IndexOf("0x") == 0)
            {
                byte[] data = new byte[1];
                Hexstr2Bytes(input.Substring(2), data);
                return data[0];
            }
            else
            {
                return byte.Parse(input);
            }
        }
        public static UInt16 Hexstr2Uint16(string input)
        {
            if (input.ToLower().IndexOf("0x") == 0)
            {
                byte[] data = new byte[2];
                Hexstr2Bytes_BE(input.Substring(2), data);
                return BitConverter.ToUInt16(data, 0);
            }
            else
            {

                return UInt16.Parse(input);
            }
        }
        public static uint Hexstr2Uint32(string input)
        {
            if (input.ToLower().IndexOf("0x") == 0)
            {
                byte[] data = new byte[4];
                Hexstr2Bytes_BE(input.Substring(2), data);
                return BitConverter.ToUInt32(data, 0);
            }
            else
            {
                return UInt32.Parse(input);
            }
        }
        public static void Hexstr2Bytes(string input, byte[] output)
        {
            for (var i = 0; i < input.Length / 2; i++)
            {
                output[i] = byte.Parse(input.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
        }
        public static void Hexstr2Bytes_BE(string input, byte[] output)
        {
            int count = input.Length / 2;
            for (var i = 0; i < count; i++)
            {
                output[count - 1 - i] = byte.Parse(input.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
        }
        public static string Bytes2Hexstr(byte[] input)
        {
            string output = "";
            for (var i = 0; i < input.Length; i++)
            {
                output += input[i].ToString("X02");
            }
            return output;
        }
        public static string Bytes2Hexstr_Revert(byte[] input)
        {
            string output = "";
            for (var i = 0; i < input.Length; i++)
            {
                output += input[input.Length - 1 - i].ToString("X02");
            }
            return output;
        }
    }

    public class Hex160
    {
        volatile byte[] bytes = new byte[20];
        public unsafe UInt32* ptr
        {
            get
            {

                unsafe
                {
                    fixed (byte* a = this.bytes)
                    {
                        return (UInt32*)a;
                    }
                }
            }
        }
        Hex160()
        {

        }

        public void Unpack(out UInt32 v0, out UInt32 v1, out UInt32 v2, out UInt32 v3, out UInt32 v4)
        {


            unsafe
            {
                fixed (byte* a = this.bytes)
                {
                    var ip = (UInt32*)a;
                    v0 = ip[0];
                    v1 = ip[1];
                    v2 = ip[2];
                    v3 = ip[3];
                    v4 = ip[4];
                }
            }

        }
        public string Format(bool withRevert0x = false)
        {
            if (withRevert0x == false)
                return HexTool.Bytes2Hexstr(bytes);
            else
                return "0x" + HexTool.Bytes2Hexstr_Revert(bytes);
        }
        public override string ToString()
        {
            throw new Exception("do not use ToString,use Format");
        }
        public static Hex160 FromString(string str, bool isRevertWith0x = true)
        {

            Hex160 hex = new Hex160();
            if (str.Length == 40)
            {
                HexTool.Hexstr2Bytes(str, hex.bytes);
            }
            else
            {
                str = str.ToLower();
                if (str.Length == 42 && str.IndexOf("0x") == 0)
                {
                    if (isRevertWith0x)
                    {
                        HexTool.Hexstr2Bytes_BE(str.Substring(2), hex.bytes);
                    }
                    else
                    {
                        HexTool.Hexstr2Bytes(str.Substring(2), hex.bytes);
                    }
                }
                else
                {
                    throw new Exception("unknown format");
                }
            }
            return hex;
        }
        public static Hex160 Zero()
        {
            return new Hex160();
        }
        public static Hex160 FromBytes(byte[] _bytes)
        {
            Hex160 hex = new Hex160();
            for (var i = 0; i < 20; i++)
            {
                hex.bytes[i] = _bytes[i];
            }
            return hex;
        }
        public static implicit operator byte[](Hex160 value)
        {
            return value.bytes;
        }
        public static implicit operator Hex160(byte[] value)
        {
            return FromBytes(value);
        }
        public override int GetHashCode()
        {
            return BitConverter.ToInt32(bytes, 0);
        }
        public override bool Equals(object obj)
        {
            unsafe
            {

                var ptr1 = this.ptr;
                var ptr2 = (obj as Hex160).ptr;
                if (ptr1[0] != ptr2[0])
                    return false;
                if (ptr1[1] != ptr2[1])
                    return false;
                if (ptr1[2] != ptr2[2])
                    return false;
                if (ptr1[3] != ptr2[3])
                    return false;
                if (ptr1[4] != ptr2[4])
                    return false;
                return true;
            }
        }
        public static bool operator ==(Hex160 a, Hex160 b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }
            return a.Equals(b);
        }
        public static bool operator !=(Hex160 a, Hex160 b)
        {
            if (!System.Object.ReferenceEquals(a, b))
            {
                return true;
            }
            return !a.Equals(b);
        }
    }
}
